# -*- coding: utf-8 -*-
import os
import json
from flask import Flask, render_template, request, make_response,send_file, abort
import hashlib
import subprocess
import re
import serial
import shutil
from pathlib import Path
import time

#定期清空黑名单
#0 0 * * * echo [] > /path/to/blacklist.json
blacklist_file = 'blacklist.json' 
max_attempts = 5


def check_images():
    images = ['natfrp/frpc', 'bltavares/zerotier','ffdfgdfg/npc']

    missing_images = []
    for image in images:
        try: 
            subprocess.check_output(['docker', 'image', 'inspect', image])
        except subprocess.CalledProcessError:  
            missing_images.append(image)
    return missing_images
#删除docker并且建立热点
def check_config():  

                
    if not os.path.exists('config.json'):
        os.system('nmcli connection delete br0')
        list_connections = subprocess.check_output(['nmcli', 'connection', 'show']).decode('utf-8')
        # 解析连接列表并删除WIFI连接
        for line in list_connections.split('\n'):
            parts = line.split()
            if len(parts) > 0 and parts[2] =='wifi' :
                # 删除连接
                subprocess.run(['nmcli', 'connection', 'delete', parts[0]])

        os.system('nmcli connection delete usb0')
        os.system('nmcli connection delete wlan0')
        os.system('nmcli connection add con-name br0 ifname br0 type bridge autoconnect yes')
        os.system("nmcli connection modify br0 ipv4.method manual ipv4.address 192.168.68.1/24 ipv4.gateway ''")
        os.system('nmcli connection add con-name usb0 ifname usb0 type ethernet master br0 autoconnect yes')
        os.system('nmcli connection add con-name wlan0 type wifi ifname wlan0 master br0 connection.autoconnect yes ssid "4GKVM" wifi-sec.key-mgmt wpa-psk wifi-sec.psk "12345678" mode ap ')
        os.system('nmcli connection up br0')
        os.system('nmcli connection up wlan0')
        os.system('systemctl enable dnsmasq.service')
        os.system('systemctl start dnsmasq.service')
        os.system('docker rm -f zerotier-one')
        os.system('docker rm -f npc')
        os.system('docker rm -f sakura')
    pass

def kill_processes():
    cmd = "pkill -f mjpg_streamer && pkill -f node"
    os.system(cmd)


def get_videodevice():
    devices = os.listdir('/dev')
    video_devices = [dev for dev in devices if re.match(r'video\d+', dev)]
    video_devices.sort()
    if len(video_devices)<3:
        return False
    return '/dev/' + video_devices[2]
def strip_all(data):
    return {key: value.strip() if isinstance(value, str) else value 
            for key, value in data.items()}

def generate_image():
  # 保存图片
    folder = '/tmp/img'
    videodevice = get_videodevice()
    img_path = './image/capture.jpg' 
    os.system('timeout 1s mjpg_streamer -i "input_uvc.so -d '+videodevice+' -r 1920x1080 -f 5" -o "output_file.so -f /tmp/img"')
    full_paths = [os.path.join('/tmp/img', f) for f in os.listdir(folder)]
    latest_img = max(full_paths, key=os.path.getmtime)
    shutil.copy(f'{latest_img}', img_path)
    shutil.rmtree(folder) 
    os.mkdir(folder)
    pass
    
def load_blacklist():
    try:
        with open(blacklist_file, 'r') as f:
            blacklist = json.load(f)
            if not blacklist:
                return {}
            return blacklist
    except FileNotFoundError:
        return {}

        
def save_blacklist(data):
    with open(blacklist_file, 'w') as f:
        json.dump(data, f)
        
def get_client_ip():
    return request.remote_addr
    
def get_md5(pass_str):
    m = hashlib.md5()
    m.update(pass_str.encode('utf-8'))
    return m.hexdigest()
def read_nodeconfig():
    try:
        with open('../server/config.json') as f:
            config_data = json.load(f)
            return  config_data
    except FileNotFoundError:
        return None
def read_kvmconfig():
    try:
        with open('config.json') as f:
            config_data = json.load(f)
            return  config_data
    except FileNotFoundError:
        return None
def handle_action(action,ser_dev):
    action_dic = {'keyandmouse':0,'keyboard':1,'poweron':2,'poweroff':3,'forceoff':4,'power_s':5,'mode':5}
    ser = serial.Serial(ser_dev, 115200 ) 
    data = bytes([247, action_dic[action], 251])
    ser.write(data)
    # 关闭串口
    ser.close()
    return True
    
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
    if not os.path.exists('/tmp/img'):
        os.makedirs('/tmp/img')
    if os.path.exists('config.json'):
        if not get_videodevice() :
            return f'''
                <script>
                    alert("采集卡未就绪，稍后刷新或检查硬件连接"); 
                </script>
                '''
        return """
        <script>
            window.onload = function() {
                var password = prompt("Please enter the password:");
                var form = document.createElement("form");
                form.method = "POST";
                form.action = "/login/login";

                var hiddenField = document.createElement("input");
                hiddenField.type = "hidden";
                hiddenField.name = "password";
                hiddenField.value = password;

                form.appendChild(hiddenField);
                document.body.appendChild(form);
                form.submit();
            };
        </script>
        """
    else:
        missing_images = check_images()
        if len(missing_images) > 0:
            return f'''
            <script>
                alert("以下镜像未安装: {missing_images}"); 
            </script>
            '''
        if request.method == 'POST':
            request.form = strip_all(request.form)
            data = {
                'admin': request.form.get('admin'),
                'plusapi': request.form.get('plusapi'),
                'kvm': request.form.get('kvm'),
                'wifi': request.form.get('wifi'),
                'wifipw': request.form.get('wifipw'),
                'nps': request.form.get('nps'),
                'npsvkey': request.form.get('npsvkey'),
                'npsport': request.form.get('npsport'),
                'zerotier': request.form.get('zerotier'),
                'sakurafrp_f': request.form.get('sakurafrp_f'),
                'sakurafrp_addr': request.form.get('sakurafrp_addr'),
                'sakurafrp_port': request.form.get('sakurafrp_port'),
            }
            data['cookie_value'] = get_md5(get_md5(request.form.get('kvm'))+'bilibili')
            
            with open('config.json', 'w') as file:
                file.write(json.dumps(data))
            # 连接wifi
            if data['wifi'] and data['wifipw'] :
                os.system(f"nmcli c add type wifi con-name {data['wifi']}   connection.autoconnect yes  ssid {data['wifi']} wifi-sec.key-mgmt wpa-psk wifi-sec.psk {data['wifipw']}")
            # 连接npc
            if data['nps'] and data['npsvkey'] :
                os.system(f"docker run -d --name npc --net=host --restart=always ffdfgdfg/npc -server={data['nps']} -vkey={data['npsvkey']}")
            # 连接zerotier

            if data['zerotier']:
                subprocess.Popen("docker run  -d  --device=/dev/net/tun --name zerotier-one --net=host --restart=always --cap-add=NET_ADMIN --cap-add=SYS_ADMIN -v /var/lib/zerotier-one:/var/lib/zerotier-one bltavares/zerotier:latest", shell=True)
                # 休眠 10 秒
                time.sleep(10)

                subprocess.Popen(f"docker exec zerotier-one zerotier-cli join {data['zerotier']}", shell=True)
            if data['sakurafrp_f'] : 

                os.system(f"docker run -d --restart=always --name=sakura natfrp/frpc  {data['sakurafrp_f']}")
            subprocess.Popen("sleep 3 && nmcli connection delete br0 && nmcli connection delete usb0&& nmcli connection delete wlan0&& reboot", shell=True)
            return render_template('completed.html')
        return render_template('index.html')
@app.route('/login/login', methods=['POST'])
def login():
    ip = get_client_ip()
    blacklist = load_blacklist()
    if ip in blacklist and blacklist[ip] >= max_attempts :
        return "IP blocked"
    file_path = "config.json"
    with open(file_path, 'r') as f:
        json_data = f.read()
    data = json.loads(json_data)
    cookie_value =data["cookie_value"] 
    config_data = read_nodeconfig()
    
    # 默认选项
    default_resolution = "1280x720"
    default_framerate = "5fps"
    default_serial = "ttyUSB0"
    
    # 如果配置文件存在，则使用配置文件中的值
    if config_data:
        default_resolution = config_data['mjpg_streamer']['res']
        default_framerate = config_data['mjpg_streamer']['fps']
        default_serial = config_data['serialport']
    

    password = request.form['password']
    password = get_md5(get_md5(password)+'bilibili')


    if password == cookie_value: 
        kill_processes()    
        image_path = './image/capture.jpg' 
        generate_image()
        # 检查是否成功
        if os.path.exists(image_path):
            image_ready = True
        else:
            image_ready = False
        response = make_response(render_template('success.html', default_resolution=default_resolution, default_framerate=default_framerate, default_serial=default_serial,image_ready=image_ready))
        response.set_cookie('auth_cookie', cookie_value)  # Replace 'your_cookie_value' with the desired value for the cookie
        return response
    else:
        blacklist = load_blacklist()
        attempts = blacklist.get(ip, 0)
        attempts += 1
        blacklist[ip] = attempts

        if attempts >= max_attempts:
            save_blacklist(blacklist)
            return "IP blocked"

        save_blacklist(blacklist)  
        return 'Invalid password'
@app.route('/login/kvm', methods=['POST'])  
def kvm():
    CONFIG_FILE = '../server/config.json'
    kvmconfig = read_kvmconfig()
    cookie_value =kvmconfig["cookie_value"] 
    cookie_request = request.cookies.get('auth_cookie')
    if cookie_request != cookie_value:
        abort(401)
    # 校验cookie
    config = read_nodeconfig()
    data = request.get_json()

    if 'action' in data:
        # 有action字段,是控制命令
        action = data['action']  
        handle_action(action,config['serialport'])
    else:
        # 没有action,是更新配置
        resolution = data['resolution']
        # 更新配置逻辑

        # 读取配置文件
        device = get_videodevice()
        need_update = False

        if data['resolution'] != config['mjpg_streamer']['res']:
            need_update = True
            config['mjpg_streamer']['res'] = data['resolution']

        if data['framerate'] != config['mjpg_streamer']['fps']:
            need_update = True
            config['mjpg_streamer']['fps'] = data['framerate']
          
        if '/dev/'+data['serial'] != config['serialport']:
            need_update = True
            config['serialport'] = '/dev/'+data['serial']
            
        if device != config['mjpg_streamer']['device']:
            need_update = True
            config['mjpg_streamer']['device'] = device      
        if need_update:
            with open(CONFIG_FILE, 'w') as f:
                json.dump(config, f)
        command = 'cd /root/open-ip-kvm && npm run start'
        shell_command = f"({command}) &"  # 使用括号将命令包裹，以便在子shell中执行
        subprocess.Popen(shell_command, shell=True)
    return 'success'

@app.route('/image/<filename>', methods=['GET'])
def serve_image(filename):
    image_path = './image/'+filename
    if os.path.exists(image_path):
        try:
            # 以二进制模式读取图片文件
            with open(image_path, 'rb') as image_file:
                image_data = image_file.read()
            # 设置响应头来指示图片的类型
            os.remove(image_path)
            # 使用 send_file 函数将图片数据发送给客户端
            response = make_response(image_data)
            # 设置响应头来指示图片的类型
            response.headers['Content-Type'] = 'image/jpeg'  
            return response


        except Exception as e:
            # 发生错误时的处理，可以根据需要进行日志记录或其他操作
            print(f"Error: {e}")
            return abort(500)  # 返回适当的错误响应码，表示服务器错误

    return 'Image not found', 404
@app.route('/reload_image')
def reload_image():
    # 重新生成和保存图片
    generate_image()
    return "ok"
check_config()

if __name__ == '__main__':
  # 检查并创建目录
  #    app.run(port=80)
    app.run(host='0.0.0.0', port=7000)